package com.zan.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zan.dao.IndexImgMapper;
import com.zan.entity.IndexImg;
import com.zan.service.IndexImgService;
import com.zan.vo.ResStatus;
import com.zan.vo.ResultVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class IndexImgServiceImpl implements IndexImgService {

    @Autowired
    private IndexImgMapper indexImgMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public ResultVo listIndexImg() {
        List<IndexImg> indexImgs = null;
        try {
            // 1000个并发请求，请求轮播图
            // string结构缓存轮播图信息
//        String imgsStr = stringRedisTemplate.opsForValue().get("indexImgs"); //跟下面一行的写法等价
            String imgsStr = stringRedisTemplate.boundValueOps("indexImgs").get();
            // 从1000个请求查询到的redis的数据都是null
            if (imgsStr != null) {
                // 从redis中获取到了轮播图信息
                JavaType javaType = objectMapper.getTypeFactory().constructParametricType(ArrayList.class, IndexImg.class);
                indexImgs = objectMapper.readValue(imgsStr, javaType);
            } else {
                // 1000个请求都会进入else
                // this表示service类，而service类在spring容器中是单例的，所以1000个并发会启动1000个线程来处理，但共用一个service实例
                synchronized (this) {
                    // 第二次查询redis
                    String s = stringRedisTemplate.boundValueOps("indexImgs").get();
                    if (s == null) {
                        // 这1000个请求中，只有第一个请求再次查询redis时依然为null
                        // 从redis中没有获取到信息，则查询数据库
                        indexImgs = indexImgMapper.selectIndexImgs();
                        // 防止缓存穿透，再进行处理
                        if (indexImgs != null) {
                            // 写入到redis中
                            stringRedisTemplate.boundValueOps("indexImgs").set(objectMapper.writeValueAsString(indexImgs));
                            // 设置过期时间
                            stringRedisTemplate.expire("indexImgs", 1, TimeUnit.DAYS);
                        } else {
                            List<IndexImg> arr = new ArrayList<>();
                            stringRedisTemplate.boundValueOps("indexImgs").set(objectMapper.writeValueAsString(arr)); // 或者用 [] 代替
                            stringRedisTemplate.expire("indexImgs", 10, TimeUnit.SECONDS);
                        }
                    } else {
                        JavaType javaType = objectMapper.getTypeFactory().constructParametricType(ArrayList.class, IndexImg.class);
                        indexImgs = objectMapper.readValue(s, javaType);
                    }
                }
            }
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }

        //返回数据
        if (indexImgs != null) {
            return new ResultVo(ResStatus.OK.code, "success", indexImgs);
        }
        return new ResultVo(ResStatus.NO.code, "fail", null);
    }
}
